其他
Windows平台下堆溢出攻击
本文为看雪论坛优秀文章
看雪论坛作者ID:1900
一
堆的作用
二
堆的创建和销毁
1、进程的默认堆
kd> dt _PEB
ntdll!_PEB
+0x018 ProcessHeap : Ptr32 Void
+0x078 HeapSegmentReserve : Uint4B
+0x07c HeapSegmentCommit : Uint4B
HANDLE WINAPI GetProcessHeap(void);
2、创建默认堆
HANDLE WINAPI HeapCreate(
__in DWORD flOptions,
__in SIZE_T dwInitialSize,
__in SIZE_T dwMaximumSize);
3、堆列表
kd> dt _PEB
ntdll!_PEB
+0x018 ProcessHeap : Ptr32 Void
+0x088 NumberOfHeaps : Uint4B
+0x08c MaximumNumberOfHeaps : Uint4B
+0x090 ProcessHeaps : Ptr32 Ptr32 Void
4、销毁堆
BOOL WINAPI HeapDestroy(__in HANDLE hHeap);
三
分配和释放堆块
1、分配堆块
#define HeapAlloc RtlAllocateHeap
PVOID
RtlAllocateHeap(
IN PVOID HeapHandle,
IN ULONG Flags,
IN SIZE_T Size
);
#define HeapReAlloc RtlReAllocateHeap
LPVOID WINAPI HeapReAlloc(
__in HANDLE hHeap,
__in DWORD dwFlags,
__in LPVOID lpMem,
__in SIZE_T dwBytes);
2、释放堆块
#define HeapFree RtlFreeHeap
BOOLEAN
RtlFreeHeap(
IN PVOID HeapHandle,
IN ULONG Flags,
IN PVOID HeapBase);
四
堆的结构
kd> dt _HEAP
ntdll!_HEAP
+0x000 Entry : _HEAP_ENTRY
+0x008 Signature : Uint4B
+0x00c Flags : Uint4B
+0x010 ForceFlags : Uint4B
+0x014 VirtualMemoryThreshold : Uint4B
+0x018 SegmentReserve : Uint4B
+0x01c SegmentCommit : Uint4B
+0x020 DeCommitFreeBlockThreshold : Uint4B
+0x024 DeCommitTotalFreeThreshold : Uint4B
+0x028 TotalFreeSize : Uint4B
+0x02c MaximumAllocationSize : Uint4B
+0x030 ProcessHeapsListIndex : Uint2B
+0x032 HeaderValidateLength : Uint2B
+0x034 HeaderValidateCopy : Ptr32 Void
+0x038 NextAvailableTagIndex : Uint2B
+0x03a MaximumTagIndex : Uint2B
+0x03c TagEntries : Ptr32 _HEAP_TAG_ENTRY
+0x040 UCRSegments : Ptr32 _HEAP_UCR_SEGMENT
+0x044 UnusedUnCommittedRanges : Ptr32 _HEAP_UNCOMMMTTED_RANGE
+0x048 AlignRound : Uint4B
+0x04c AlignMask : Uint4B
+0x050 VirtualAllocdBlocks : _LIST_ENTRY
+0x058 Segments : [64] Ptr32 _HEAP_SEGMENT
+0x158 u : __unnamed
+0x168 u2 : __unnamed
+0x16a AllocatorBackTraceIndex : Uint2B
+0x16c NonDedicatedListLength : Uint4B
+0x170 LargeBlocksIndex : Ptr32 Void
+0x174 PseudoTagEntries : Ptr32 _HEAP_PSEUDO_TAG_ENTRY
+0x178 FreeLists : [128] _LIST_ENTRY
+0x578 LockVariable : Ptr32 _HEAP_LOCK
+0x57c CommitRoutine : Ptr32 long
+0x580 FrontEndHeap : Ptr32 Void
+0x584 FrontHeapLockCount : Uint2B
+0x586 FrontEndHeapType : UChar
+0x587 LastSegmentIndex : UChar
kd> dt _HEAP_ENTRY
ntdll!_HEAP_ENTRY
+0x000 Size : Uint2B
+0x002 PreviousSize : Uint2B
+0x004 SmallTagIndex : UChar
+0x005 Flags : UChar
+0x006 UnusedBytes : UChar
+0x007 SegmentIndex : UChar
HEAP_ENTRY_BUSY(0x1):该块处于占用状态
HEAP_ENTRY_EXTRA_PRESENT(0x2):这个块存在额外描述
HEAP_ENTRY_FILL_PATTERN(0x4):使用固定模式填充堆块
HEAP_ENTRY_VIRTUAL_ALLOC(0x8):虚拟分配
HEAP_ENTRY_LAST_ENTRY(0x10):该段的最后一个块
kd> dt _HEAP_FREE_ENTRY
ntdll!_HEAP_FREE_ENTRY
+0x000 Size : Uint2B
+0x002 PreviousSize : Uint2B
+0x000 SubSegmentCode : Ptr32 Void
+0x004 SmallTagIndex : UChar
+0x005 Flags : UChar
+0x006 UnusedBytes : UChar
+0x007 SegmentIndex : UChar
+0x008 FreeList : _LIST_ENTRY
五
漏洞原理
1、漏洞成因
#include <cstdio>
#include <windows.h>
int main()
{
HANDLE hHeap = NULL;
PVOID h1 = NULL, h2 = NULL, h3 = NULL;
printf("Create\n");
system("pause");
hHeap = HeapCreate(NULL, 0x1000, 0x10000);
h1 = HeapAlloc(hHeap, HEAP_ZERO_MEMORY, 0x8);
h2 = HeapAlloc(hHeap, HEAP_ZERO_MEMORY, 0x8);
h3 = HeapAlloc(hHeap, HEAP_ZERO_MEMORY, 0x8);
printf("Free\n");
HeapFree(hHeap, 0, h2); // 为了防止合并,释放第二个
printf("Allocate again\n");
h2 = HeapAlloc(hHeap, HEAP_ZERO_MEMORY, 0x8);
system("pause");
return 0;
}
printf("Allocate again\n");
char szExp[0x18] = { 0 };
memset(szExp, 'A', 0x18);
memcpy(h1, szExp, sizeof(szExp));
h2 = HeapAlloc(hHeap, HEAP_ZERO_MEMORY, 0x8);
2、漏洞利用
rounded_size = ROUND_SIZE(size);
if (rounded_size < HEAP_MIN_DATA_SIZE) rounded_size = HEAP_MIN_DATA_SIZE;
#define HEAP_MIN_DATA_SIZE 16
/* Locate a suitable free block */
if (!(pArena = HEAP_FindFreeBlock( heapPtr, rounded_size, &subheap )))
{
TRACE("(%p,%08lx,%08lx): returning NULL\n",
heap, flags, size );
if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveHeapLock( &heapPtr->critSection );
if (flags & HEAP_GENERATE_EXCEPTIONS) RtlRaiseStatus( STATUS_NO_MEMORY );
return NULL;
}
/* Remove the arena from the free list */
list_remove( &pArena->entry );
/* remove an element from its list */
__inline static void list_remove( struct list *elem )
{
elem->next->prev = elem->prev;
elem->prev->next = elem->next;
}
.text:0040D456 mov ecx, [ebp+pList]
.text:0040D459 mov edx, [ecx+4] ; pList->prev赋给edx
.text:0040D45C mov eax, [ebp+pList]
.text:0040D45F mov ecx, [eax] ; pList->next赋给eax
.text:0040D461 mov [edx], ecx
.text:0040D456 mov ecx, [ebp+pList]
.text:0040D459 mov edx, [ecx+4] ; pList->prev赋给edx,此时edx为保存返回地址的栈地址
.text:0040D45C mov eax, [ebp+pList]
.text:0040D45F mov ecx, [eax] ; pList->next保存了shellcode地址,这句话等价于 mov ecx, dwShellCodeAddr
.text:0040D461 mov [edx], ecx ; 等价于 mov [dwRetAddr], ecx 这里的dwRetAddr是保存了返回地址的栈地址
.text:0040D448 mov eax, [ebp+pList]
.text:0040D44B mov ecx, [eax]
.text:0040D44D mov edx, [ebp+pList]
.text:0040D450 mov eax, [edx+4]
.text:0040D453 mov [ecx+4], eax
栈帧中的函数返回地址:函数返回时,跳去执行shellcode
栈帧中的SEH句柄:异常发生时,跳去执行shellcode
重要函数调用地址:函数调用时,跳去执行shellcode
__inline static void list_remove( struct list *elem )
{
if (elem->next->prev == node && elem->prev->next == node)
{
elem->next->prev = elem->prev;
elem->prev->next = elem->next;
}
}
六
参考资料
《0day安全:软件漏洞分析技术》
《软件调试》(第二版)
看雪ID:1900
https://bbs.pediy.com/user-home-835440.htm
# 往期推荐
3.PWN学习总结
球分享
球点赞
球在看
点击“阅读原文”,了解更多!